import abc
from collections import OrderedDict
from dataclasses import dataclass
from functools import cmp_to_key
from typing import TypeVar, Generic, List, Callable, Any, Union, Dict

from emi.core.Runner import RunnerContext

InputData = TypeVar('InputData')
OutputData = TypeVar('OutputData')

@dataclass
class SortItem(Generic[OutputData]):
    """
    处理排序的条目
    """

    name:str = ""    ## 条目
    reverse:bool = False   ## 是否反序
    value_fun:Callable[[OutputData],any] = None   ## 获取DataItem的排序值，可以是lambda表达式

    def get_sort_value(self,data:OutputData):
        if self.value_fun:
            ret =  self.value_fun(data)
            if isinstance(ret,set):
                return ret.pop()
            return ret
        return None

@dataclass
class MatchItem(Generic[OutputData]):

    name: str = ""  ## 条目
    match_fun: Callable[[OutputData], any] = None  ## 获取DataItem的排序值，可以是lambda表达式

    def is_match(self,data:OutputData)->bool:
        if self.match_fun:
            ret =  self.match_fun(data)
            if isinstance(ret,set):
                return ret.pop()
            return ret
        return None


@dataclass
class OptionItem:

    name:str
    label:str
    datas:List
    value:Any = None


DEFAULT_SROT_ITEM = SortItem(name="default", value_fun=lambda x: 0)  ## 默认排序选项

class DataLoader(Generic[InputData,OutputData]):

    """
    DataLoader 数据加载器。其中，类型InputData、OutputData 分别是输入、输出对象类型。
    数据加载过程中，整合了通用的排序(SortItem)、匹配过滤(MatchItem)功能，方便外部灵活调用控制，加载器只需关注加载数据就行。
    """
    @staticmethod
    def make(data_list):
        class _InnerDataLoader(DataLoader):

            def _do_load(self, runner_context: RunnerContext, options: Dict) -> List[OutputData]:
                #print(f"xxxxx:{options['xxxx']}")
                return data_list
        return _InnerDataLoader()

    def __init__(self,input:InputData = None):
        self._sort_item_map = {}
        self._match_item_map = {}
        self._input:InputData = input
        self._option_items :Dict[str,OptionItem]= OrderedDict()
        self._on_init()

    def reset(self):
        for option_item in self._option_items.values():
            for d in option_item.datas:
                if d.get("selected"):
                    option_item.value = d['value']

        self._sort_item_map.clear()  ## 清空排序状态


    def _on_init(self):
        pass

    def on_select_option_changed(self,name,new_value):
        pass

    def get_input(self)->InputData:
        return self._input

    def get_option_items(self)->List[OptionItem]:
        return list(self._option_items.values())

    def set_option_value(self,name:str,value:Any):
        option_item = self._option_items[name]
        for d in option_item.datas:
            if d['value'] == value:
                d['selected'] = True
            else:
                d['selected'] = False
        option_item.value = value
        self.on_select_option_changed(name,value)

    def set_option_datas(self,name:str,label:str,option_datas:List[Dict]):
        """
        :param option_datas   pywebio的SelectOptions，格式如下
                  {
                    "value": day.toordinal(),
                    "label": day.__str__() if has_content else f"{day.__str__()}(空)",
                    "selected": index == 0
                }
        """
        option_item = OptionItem(name=name,label=label,datas=option_datas)
        for d in option_datas:
            if d.get("selected"):
                option_item.value = d['value']
        self._option_items[option_item.name] = option_item

    @abc.abstractmethod
    def _do_load(self,runner_context: RunnerContext,options:Dict)-> List[OutputData]:
        """
        加载数据
        :param input:
        :param options 参数值，从set_option_datas中设置而来
        :param runner_context:
        :return:
        """
        raise NotImplementedError

    def load(self,runner_context: RunnerContext = None,do_match = True,do_sort:bool = True) -> List[OutputData]:
        """
        加载数据对象
        :param input:  输入数据对象，即数据源
        :param runner_context: 运行环境
        :param do_match: 是否执行通用匹配功能
        :param do_sort: 是否执行通用排序功能
        :return:
        """
        options = {}
        for op in self._option_items.values():
            options[op.name] = op.value
        output_list = self._do_load(runner_context,options=options)
        if not output_list:
            return output_list
        if do_match:
            output_list = self._do_match(output_list)
        if do_sort:
            self._do_sort(output_list)
        return output_list



    def get_sort_item(self, level):
        """
        返回排序选项
        :param level: 排序等级:1,2,3
        :return:
        """
        return self._sort_item_map.get(level)

    def set_sort_item(self,level:int,sort_item:SortItem[OutputData]):
        """
        设置第1，2，3级的排序选项。
        :param level:
        :param sort_item:
        :return:
        """
        if level < 1 or level > 3:
            raise RuntimeError("排序 level 只支持1，2，3级别")
        self._sort_item_map[level] = sort_item

    def get_match_item(self,name:str):
        """
        获取一个匹配项目。
        :param name:
        :return:
        """
        return self._match_item_map.get(name)

    def add_match_item(self, item: MatchItem):
        self._match_item_map[item.name] = item

    def remove_match_item(self,item:Union[str,MatchItem]):
        if isinstance(item,str):
            item_name = item
        else:
            item_name = item.name
        del self._match_item_map[item_name]

    def _do_match(self,data_items:List[OutputData])->List[OutputData]:
        """
        匹配过滤输出值
        :param data_items:
        :return:
        """

        match_items:List[MatchItem] = [ match_item for match_item in self._match_item_map.values() if match_item is not None]
        if not match_items:
            ## 没有设置匹配参数，不需要过滤
            return data_items
        output_list = []
        for data in data_items:
            for match_item in match_items:
                if not match_item.is_match(data):
                    continue
                output_list.append(data)
        return output_list

    def get_sort_item_list(self):
        sort_levels = list(self._sort_item_map.keys())
        if not sort_levels:
            ## 没有设置排序参数，不需要排序
            return
        sort_levels.sort(reverse=False)
        sort_items: List[SortItem] = []
        for sort_level in sort_levels:
            sort_item = self._sort_item_map[sort_level]
            if sort_item:
                sort_items.append(sort_item)
        return sort_items

    def _do_sort(self,data_items:List[OutputData]):
        """

        :param data_items:
        :return:
        """
        sort_items = self.get_sort_item_list()
        if not sort_items:
            ## 没有设置排序参数，不需要排序
            return

        def _inner_cmp_(x, y):
            for sort_item in sort_items:
                x_value = sort_item.get_sort_value(x)
                y_value = sort_item.get_sort_value(y)
                if x_value != y_value:
                    if sort_item.reverse:
                        return y_value - x_value
                    else:
                        return x_value - y_value
            return 0
        data_items.sort(key=cmp_to_key(_inner_cmp_), reverse=False)


if __name__ == '__main__':

    def get_lambada_value(lambada,*value):
        print(f"{lambada}")
        v = lambada(*value)
        if isinstance(v,set):
            return v.pop()
        return v

    f = lambda a,b:a+b

    v = get_lambada_value(f,3,4)

    print(f"{v}")



